package com.cipe.cmrs.util;

import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ValidationUtils;

/**
 * ValidationUtil extends from org.springframework.validation.ValidationUtils to use common
 * validation methods in this class
 * @author Incepio
 *
 */

public class ValidationUtil extends ValidationUtils {

	private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+([-][A-Za-z0-9]+)*(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
	private static final Pattern pattern = Pattern.compile(EMAIL_PATTERN);;
	private static Matcher matcher;

	/**
	 * 
	 * @param errors
	 * @param field
	 * @param errorCode
	 * @param minLength
	 * checks the minimum length required of the field and if field is null or it's length less then
	 *  minLength its add a field level error in errors object if no other error exists in errors
	 *  related to field
	 */
	public static void checkMinLength(Errors errors, String field, String errorCode, String minLength){
		checkMinLength(errors, field, errorCode, null, Integer.valueOf(minLength));
	}

	public static void checkMinLength(Errors errors, String field, String errorCode, Object[] errorArgs, String minLength){
		checkMinLength(errors, field, errorCode, errorArgs, Integer.valueOf(minLength));
	}

	public static void checkMinLength(Errors errors, String field, String errorCode, Object[] errorArgs, int minLength){
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		Object value = errors.getFieldValue(field);

		if(value != null && !checkMinLength(value.toString(), minLength)){
			errors.rejectValue(field, errorCode, errorArgs, null);
		}
	}

	public static boolean checkMinLength(String field, int minLength){

		if(field != null && field.length() < minLength){
			return false;
		} else {
			return true;
		}
	}


	/**
	 * 
	 * @param errors
	 * @param field
	 * @param errorCode
	 * @param maxLength
	 * checks the maximum length required of the field and if field's length greater then
	 *  maxLength its add a field level error in errors object if no other error exists in errors
	 *  related to field
	 */
	public static void checkMaxLength(Errors errors, String field, String errorCode,  String maxLength){
		checkMaxLength(errors, field, errorCode, null, Integer.valueOf(maxLength));
	}

	public static void checkMaxLength(Errors errors, String field, String errorCode, Object[] errorArgs,  String maxLength){
		checkMaxLength(errors, field, errorCode, errorArgs, Integer.valueOf(maxLength));
	}

	public static void checkMaxLength(Errors errors, String field, String errorCode, Object[] errorArgs, int maxLength){
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		Object value = errors.getFieldValue(field);

		if(value != null && !checkMaxLength(value.toString(), maxLength)){
			errors.rejectValue(field, errorCode, errorArgs, null);
		}
	}

	public static boolean checkMaxLength(String field, int maxLength){

		if(field != null && field.length() > maxLength){
			return false;
		} else {
			return true;
		}
	}

	/**
	 * 
	 * @param errors
	 * @param field
	 * @param errorCode
	 * @param minLength
	 * @param maxLength
	 * checks the minimum and maximum length required of the field.
	 * if it calls checkMinLength and checkMaxLength and its add a field level error in errors object
	 * if no other error exists in errors related to field
	 */
	public static void checkLengthRange(Errors errors, String field, String errorCode, String minLength, String maxLength){
		checkLengthRange(errors, field, errorCode, null, Integer.valueOf(minLength), Integer.valueOf(maxLength));
	}

	public static void checkLengthRange(Errors errors, String field, String errorCode, Object[] errorArgs, String minLength, String maxLength){
		checkLengthRange(errors, field, errorCode, errorArgs, Integer.valueOf(minLength), Integer.valueOf(maxLength));
	}

	public static void checkLengthRange(Errors errors, String field, String errorCode, Object[] errorArgs, int minLength, int maxLength){
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		Object value = errors.getFieldValue(field);

		if(value != null && !checkLengthRange(value.toString(), minLength, maxLength)){
			errors.rejectValue(field, errorCode, errorArgs, null);
		}
	}

	public static boolean checkLengthRange(String field, int minLength, int maxLength){

		return checkMinLength(field, minLength) && checkMaxLength(field, maxLength);
	}


	/**
	 * 
	 * @param errors
	 * @param field
	 * @param errorCode
	 * @param minLength
	 * checks the minimum length required of the field and if field is null or it's length less then
	 *  minLength its add a field level error in errors object if no other error exists in errors
	 *  related to field
	 */
	public static void checkMinValue(Errors errors, String field, String errorCode, String minValue){
		checkMinValue(errors, field, errorCode, null, Double.valueOf(minValue));
	}

	public static void checkMinValue(Errors errors, String field, String errorCode, Object[] errorArgs, String minValue){
		checkMinValue(errors, field, errorCode, errorArgs, Double.valueOf(minValue));
	}

	public static void checkMinValue(Errors errors, String field, String errorCode, Object[] errorArgs, double minValue){
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		Object value = errors.getFieldValue(field);

		if(value != null && !checkMinValue((Double) value, minValue)){
			errors.rejectValue(field, errorCode, errorArgs, null);
		}
	}

	public static boolean checkMinValue(Double field, double minValue){

		if(field != null && field < minValue){
			return false;
		} else {
			return true;
		}
	}


	/**
	 * 
	 * @param errors
	 * @param field
	 * @param errorCode
	 * @param maxLength
	 * checks the maximum length required of the field and if field's length greater then
	 *  maxLength its add a field level error in errors object if no other error exists in errors
	 *  related to field
	 */
	public static void checkMaxValue(Errors errors, String field, String errorCode, String maxValue){
		checkMaxValue(errors, field, errorCode, null, Double.valueOf(maxValue));
	}

	public static void checkMaxValue(Errors errors, String field, String errorCode, Object[] errorArgs,  String maxValue){
		checkMaxValue(errors, field, errorCode, errorArgs, Double.valueOf(maxValue));
	}

	public static void checkMaxValue(Errors errors, String field, String errorCode, Object[] errorArgs, double maxValue){
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		Object value = errors.getFieldValue(field);

		if(value != null && !checkMaxValue((Double) value, maxValue)){
			errors.rejectValue(field, errorCode, errorArgs, null);
		}
	}

	public static boolean checkMaxValue(Double field, double maxValue){

		if(field != null && field > maxValue){
			return false;
		} else {
			return true;
		}
	}

	/**
	 * 
	 * @param errors
	 * @param field
	 * @param errorCode
	 * @param minLength
	 * @param maxLength
	 * checks the minimum and maximum length required of the field.
	 * if it calls checkMinLength and checkMaxLength and its add a field level error in errors object
	 * if no other error exists in errors related to field
	 */
	public static void checkValueRange(Errors errors, String field, String errorCode, String minValue, String maxValue){
		checkValueRange(errors, field, errorCode, null, Double.valueOf(minValue), Double.valueOf(maxValue));
	}

	public static void checkValueRange(Errors errors, String field, String errorCode, Object[] errorArgs, String minValue, String maxValue){
		checkValueRange(errors, field, errorCode, errorArgs, Double.valueOf(minValue), Double.valueOf(maxValue));
	}

	public static void checkValueRange(Errors errors, String field, String errorCode, Object[] errorArgs, double minValue, double maxValue){
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		Object value = errors.getFieldValue(field);
		if(value == null){
			return;
		}
		double doubleValue = value instanceof Integer ? (Integer)value : (Double)value;
		if(!checkValueRange(doubleValue, minValue, maxValue)){
			errors.rejectValue(field, errorCode, errorArgs, null);
		}
	}

	public static boolean checkValueRange(Double field, double minValue, double maxValue){

		return checkMinValue(field, minValue) && checkMaxValue(field, maxValue);
	}
	
	public static void checkNotEquals(Errors errors, String field1, String field2, String errorCode){
		checkNotEquals(errors, field1, field2, errorCode, null);
	}
	
	public static void checkNotEquals(Errors errors, String field1, String field2, String errorCode, Object[] errorArgs){

		if(isErrorAlreadyExists(errors, field1) || isErrorAlreadyExists(errors, field2)) {
			return;
		}

		Object value1 = errors.getFieldValue(field1);
		Object value2 = errors.getFieldValue(field2);

		if(value1 == null && value2 == null){
			return;
		}
		else if (value1 == null || value2 == null || value1.toString().equals(value2.toString())) {
			errors.rejectValue(field2, errorCode, errorArgs, null);
		}
	}

	public static void checkEquals(Errors errors, String field1, String field2, String errorCode){
		checkEquals(errors, field1, field2, errorCode, null);
	}

	public static void checkEquals(Errors errors, String field1, String field2, String errorCode, Object[] errorArgs){

		if(isErrorAlreadyExists(errors, field1) || isErrorAlreadyExists(errors, field2)) {
			return;
		}

		Object value1 = errors.getFieldValue(field1);
		Object value2 = errors.getFieldValue(field2);

		if(value1 == null && value2 == null){
			return;
		}
		else if (value1 == null || value2 == null || !value1.toString().equals(value2.toString())) {
			errors.rejectValue(field2, errorCode, errorArgs, null);
		}
	}

	/**
	 * 
	 * @param errors
	 * @param email
	 * @param errorCode
	 * validate the email address if email address is null or not a valid email address then
	 * its add a field level error in errors object if no other error exists in errors related to field
	 */

	public static void validateEmail(Errors errors, String email, String errorCode){
		validateEmail(errors, email, errorCode, null);
	}

	public static void validateEmail(Errors errors, String email, String errorCode, Object[] errorArgs){
		if(isErrorAlreadyExists(errors, email)) {
			return;
		}

		Object value = errors.getFieldValue(email);

		if(value == null || !validateEmail(value.toString())){
			errors.rejectValue(email, errorCode, errorArgs, null);
		}

	}


	public static boolean validateEmail(String email){

		if(email == null){
			return false;
		}
		matcher = pattern.matcher(email);
		return matcher.matches();
	}

	public static void checkEqualsAddToField(Errors errors, String field1, String field2, String errorCode, String addField){
		checkEqualsAddToField(errors, field1, field2, errorCode, null, addField);
	}

	public static void checkEqualsAddToField(Errors errors, String field1, String field2, String errorCode, Object[] errorArgs, String addField){

		if(isErrorAlreadyExists(errors, field1) || isErrorAlreadyExists(errors, field2)) {
			return;
		}

		Object value1 = errors.getFieldValue(field1);
		Object value2 = errors.getFieldValue(field2);

		if(value1 == null && value2 == null){
			return;
		}
		else if (value1 == null || value2 == null || !value1.toString().equals(value2.toString())) {
			errors.rejectValue(addField, errorCode, errorArgs, null);
		}
	}

	public static void checkEqualsIgnoreCaseAddToField(Errors errors, String field1, String field2, String errorCode, String addField){
		checkEqualsIgnoreCaseAddToField(errors, field1, field2, errorCode, null, addField);
	}

	public static void checkEqualsIgnoreCaseAddToField(Errors errors, String field1, String field2, String errorCode, Object[] errorArgs, String addField){

		if(isErrorAlreadyExists(errors, field1) || isErrorAlreadyExists(errors, field2)) {
			return;
		}

		Object value1 = errors.getFieldValue(field1);
		Object value2 = errors.getFieldValue(field2);

		if(value1 == null && value2 == null){
			return;
		}
		else if (value1 == null || value2 == null || !value1.toString().equalsIgnoreCase(value2.toString())) {
			errors.rejectValue(addField, errorCode, errorArgs, null);
		}
	}


	public static void validateDateBefore(Errors errors, String field, String errorCode, Date dateAfter) {
		validateDateBefore(errors, field, errorCode, null, dateAfter);
	}


	public static void validateDateBefore(Errors errors, String field, String errorCode, Object[] errorArgs, Date dateAfter) {
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		Object value = errors.getFieldValue(field);

		if(value != null && dateAfter != null &&  !((Date)value).before(dateAfter)){
			errors.rejectValue(field, errorCode, errorArgs, null);
		}
	}

	public static void validateDateEqualsBefore(Errors errors, String field, String errorCode, Object[] errorArgs, Date dateBefore, Date dateAfter) {
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		if(dateBefore != null && dateAfter != null && !dateBefore.equals(dateAfter) && !dateBefore.before(dateAfter)){
			FieldError error = new FieldError(errors.getObjectName(), field, null, false, new String[]{errorCode}, errorArgs, null);
			((BindException)errors).addError(error);
		}
	}

	public static void validateDateBefore(Errors errors, String field, String errorCode, Object[] errorArgs, Date dateBefore, Date dateAfter) {
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		if(dateBefore != null && dateAfter != null && !dateBefore.before(dateAfter)){
			FieldError error = new FieldError(errors.getObjectName(), field, null, false, new String[]{errorCode}, errorArgs, null);
			((BindException)errors).addError(error);
		}
	}

	public static void validateDateEqualsAfter(Errors errors, String field, String errorCode, Object[] errorArgs, Date dateAfter, Date dateBefore) {
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		if(dateBefore != null && dateAfter != null && !dateAfter.equals(dateBefore) && !dateAfter.after(dateBefore)){
			FieldError error = new FieldError(errors.getObjectName(), field, null, false, new String[]{errorCode}, errorArgs, null);
			((BindException)errors).addError(error);
		}
	}

	public static void validateDateAfter(Errors errors, String field, String errorCode, Object[] errorArgs, Date dateAfter, Date dateBefore) {
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		if(dateBefore != null && dateAfter != null && !dateAfter.after(dateBefore)){
			FieldError error = new FieldError(errors.getObjectName(), field, null, false, new String[]{errorCode}, errorArgs, null);
			((BindException)errors).addError(error);
		}
	}


	public static void rejectIfNull(Errors errors, String field, String errorCode) {
		rejectIfNull(errors, field, errorCode, new Object[]{});
	}


	public static void rejectIfNull(Errors errors, String field, String errorCode, Object[] errorArgs) {
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}

		Object value = errors.getFieldValue(field);

		if(value == null){
			errors.rejectValue(field, errorCode, errorArgs, null);
		}

	}

	public static void rejectIfNull(Errors errors, String field, String errorCode, Object value) {
		rejectIfNull(errors, field, errorCode, null, value);
	}

	public static void rejectIfNull(Errors errors, String field, String errorCode, Object[] errorArgs, Object value) {
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}
		if(value == null){
			FieldError error = new FieldError(errors.getObjectName(), field, null, false, new String[]{errorCode}, errorArgs, null);
			((BindException)errors).addError(error);
		}
	}

	/**
	 * 
	 * @param errors
	 * @param field
	 * @return true if field level error exists already exists in errors object related to field
	 */

	public static boolean isErrorAlreadyExists(Errors errors, String field) {
		if(errors.getFieldError(field) == null) {
			return false;
		} else {
			return true;
		}
	}


	public static void rejectIfFalse(Errors errors, String field, String errorCode) {
		if(isErrorAlreadyExists(errors, field)) {
			return;
		}
		Object value = errors.getFieldValue(field);
		if(value != null && value instanceof Boolean && ((Boolean)value) == false){
			errors.rejectValue(field, errorCode);
		}
	}

}
